Skip to content

Add WASI build support and npm package#3

Merged
NikhilVerma merged 7 commits intomasterfrom
wasi-build
Feb 4, 2026
Merged

Add WASI build support and npm package#3
NikhilVerma merged 7 commits intomasterfrom
wasi-build

Conversation

@NikhilVerma
Copy link

@NikhilVerma NikhilVerma commented Feb 4, 2026

Summary

  • Adds WASI (WebAssembly System Interface) build support for regula
  • Creates npm-publishable package (regula-wasi) that runs via Node.js 18+
  • Keeps patches separate so both native and WASI builds work from same codebase

New Files

File Purpose
build-wasi.sh Build script that applies vendor patches and compiles to WASI
patches/ WASI-compatible vendor patches (applied only during WASI build)
package.json npm package configuration
index.js Programmatic API (runRegula, validate functions)
cli.js CLI wrapper for the WASM binary
test/ Test suite including WASI/native parity tests

Usage

# Build WASI binary (70MB)
./build-wasi.sh

# Build native binary (unchanged)
go build -mod vendor -o bin/regula

# Run tests
npm test           # Basic API tests
npm run test:wasi  # Parity tests vs native binary

Test Results

  • Basic API tests: 6/6 passed
  • WASI/native parity tests: 10/10 passed
  • Rego lib tests: identical results (34/57 pass) on both builds

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • NPM package distribution (regula-wasi v3.2.3) for Node.js 18+
    • WebAssembly/WASI runtime support enabling cross-platform execution
    • Programmatic Node.js API for integrating Regula into applications
  • Documentation

    • Comprehensive README updates covering npm installation, WASI setup, CLI usage, and programmatic API examples
    • Security patch notes and vendor updates documentation
  • Tests

    • New test suites validating functionality and parity across implementations

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

Walkthrough

This change adds WASI (WebAssembly System Interface) support and npm packaging to Regula. New files include a shell build script that vendors dependencies with patches, then compiles a WASI-compatible WebAssembly binary. A Node.js CLI entry point boots the WASM module under WASI, while an index.js wrapper exposes programmatic API functions. Multiple dependency patches enable WASI compatibility (terminal stubs, errno handling, file watcher stubs). Package.json, .npmignore, and .gitignore configure npm distribution. Test fixtures and harnesses validate parity between WASI and native builds. Documentation restructures to emphasize fork identity and WASI/npm usage.

Sequence Diagram(s)

sequenceDiagram
    participant User as Node.js User
    participant CLI as cli.js
    participant WASM as regula.wasm
    participant WASI as WASI Runtime
    participant FS as File System

    User->>CLI: node cli.js [args]
    CLI->>CLI: Create WASI instance
    CLI->>CLI: Read regula.wasm
    CLI->>WASM: Instantiate with WASI imports
    WASM->>WASI: Initialize (get env, args, fd:0/1/2)
    WASI->>FS: Preopen "/" (read paths)
    WASM->>WASM: Execute Regula logic
    WASM->>FS: Read input files
    WASM->>WASM: Analyze & validate
    WASM->>WASI: Write output (stdout)
    WASI->>CLI: Return exit code
    CLI->>User: Print results, exit
Loading
sequenceDiagram
    participant Caller as Caller (Node.js)
    participant IndexJS as index.js
    participant ChildProcess as Child Process
    participant CLI as cli.js
    participant WASM as regula.wasm

    Caller->>IndexJS: runRegula(paths, options)
    IndexJS->>IndexJS: Normalize paths & build args
    IndexJS->>ChildProcess: spawn "node cli.js"
    ChildProcess->>CLI: Execute with args
    CLI->>WASM: Boot WASI module
    WASM->>WASM: Run analysis
    WASM->>ChildProcess: Output JSON
    ChildProcess->>IndexJS: Return stdout/stderr/code
    IndexJS->>IndexJS: Parse JSON stdout
    IndexJS->>Caller: Return parsed results
Loading

Poem

🌊 WASI winds blow through the code,
Patches patched the vendor's road,
From Go to Node, a WASM quest,
Now npm holds your test—
Regula flies in browsers' best! 🚀

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly and concisely summarizes the main change: adding WASI build support and npm packaging for regula. It directly reflects the PR's core objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch wasi-build

Comment @coderabbitai help to get the list of available commands and usage tips.

NikhilVerma and others added 2 commits February 4, 2026 18:28
This adds the ability to build regula as a WASI (WebAssembly System Interface)
binary that can be run via Node.js 18+.

New files:
- build-wasi.sh: Build script that applies vendor patches and compiles to WASI
- patches/: WASI-compatible vendor patches (applied only during WASI build)
- package.json: npm package configuration (regula-wasi)
- index.js: Programmatic API (runRegula, validate functions)
- cli.js: CLI wrapper for the WASM binary
- test/: Test suite including WASI/native parity tests

Usage:
- Build WASI: ./build-wasi.sh
- Build native: go build -mod vendor (unchanged)
- Run tests: npm test && npm run test:wasi

The WASI build produces identical output to the native binary.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add LICENSE to published files
- Update description to note it's a fork
- Point repository URL to nonfx/regula

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
NikhilVerma and others added 2 commits February 4, 2026 18:34
- Update build-wasi.sh to run go mod vendor first
- Add -ldflags for stripped builds
- Skip patches that don't apply (go-systemd removed)
- Update fsnotify patch for new package structure

Requires Go 1.25+ for WASM linking of large binaries.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Document this as a maintained fork of fugue/regula
- List security patches and dependency upgrades
- Add WASI build documentation
- Add npm installation and usage instructions
- Update available commands and options

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In `@cli.js`:
- Around line 13-18: The WASI instance currently preopens the entire host
filesystem via preopens: { "/": "/" } in the new WASI(...) call; change that to
only expose the current working directory by replacing the preopens mapping with
a single subtree like preopens: { ".": process.cwd() } (i.e., update the new
WASI(...) creation in cli.js so the WASI constructor maps only process.cwd()
instead of "/").

In `@index.js`:
- Around line 12-26: The function runRegula documents options.format but always
forces JSON; update argument construction in runRegula so args uses
options.format when provided (fall back to "json" if not), i.e., replace the
hardcoded ["run","--format","json"] with logic that sets "--format" to
options.format or "json" before pushing it into args; reference runRegula, the
args array, and options.format to locate and change the code.

In `@patches/coreos/go-systemd/journal/journal.go`:
- Around line 72-82: Enabled() currently calls net.Dial("unixgram",
journalSocket) but discards the resulting connection, leaking a file descriptor
on each probe; update Enabled() to capture the returned connection (e.g., conn,
err := net.Dial(...)), check err as now, and ensure the connection is closed
(conn.Close()) before returning (use a defer immediately after a successful dial
or an explicit Close prior to return) so repeated calls don't exhaust FDs; keep
the existing checks involving onceConn.Do(initConn) and unixConnPtr unchanged.
- Around line 120-137: The temp file fallback currently drops large journal
entries because the ancillary-FD send using conn.WriteMsgUnix is commented out;
replace that silent noop with a build-tagged helper (e.g., sendWithFD or sendFD)
and call it where the commented code is: create sendWithFD_unix.go (//go:build
!wasip1) that constructs syscall.UnixRights(int(file.Fd())) and calls
conn.WriteMsgUnix to send the FD and returns any error, and create
sendWithFD_wasip1.go (//go:build wasip1) that returns a clear "unsupported on
wasip1" error; update the code around tempFd/conn.WriteMsgUnix to call this
helper and propagate its error instead of returning nil so large entries are
either sent or fail explicitly.

In `@patches/fsnotify/fsnotify/backend_other.go`:
- Around line 22-69: The Close, Add and Remove methods on Watcher currently
return nil on unsupported platforms and should instead return the same
unsupported-backend error used by NewWatcher so misuse doesn't silently succeed;
update Watcher.Close, Watcher.Add and Watcher.Remove to return the identical
error value (the one NewWatcher returns when the backend is unsupported) rather
than nil, preserving signatures and without adding platform-specific behavior.

In `@patches/sirupsen/logrus/terminal_check_notappengine.go`:
- Around line 11-17: The call to isTerminal(int(v.Fd())) in checkIfTerminal is
undefined; add a concrete implementation or delegate to the standard term
package. Import "golang.org/x/term" (or "golang.org/x/crypto/ssh/terminal" if
you prefer) and replace/implement isTerminal to call term.IsTerminal(fd) (or add
a local isTerminal wrapper that calls term.IsTerminal) so checkIfTerminal
compiles; ensure the import is added and referenced functions are
checkIfTerminal and isTerminal (and types io.Writer/os.File) so the linker can
resolve the symbol.

In `@test/test-wasi.js`:
- Around line 178-187: The test named "Exit codes match for failures" currently
only compares failure detection; update it to also assert the exit codes from
runWasi and runBinary are equal and non-zero when failures are present: after
computing wasiHasFailures and binaryHasFailures, add assertions comparing
wasi.exitCode and binary.exitCode (e.g., assert.strictEqual(wasi.exitCode,
binary.exitCode)) and assert that when wasiHasFailures is true the exit code is
!== 0 (and similarly for binary) so both the equality and non-zero behavior are
checked for the wasi and binary results returned by runWasi and runBinary.
- Around line 36-49: runWasi and runBinary currently always resolve even when
the child process fails, so tests can pass by comparing stdout alone; update the
helpers or tests so failures are surfaced: modify runWasi and runBinary to
reject when the execFile callback reports an error (use error and error.code) so
callers receive a rejected promise on non-zero exit, or alternatively keep the
helpers but add explicit assertions in success tests (e.g., Test 10) to assert
result.exitCode === 0 before checking stdout/JSON; locate the functions runWasi
and runBinary and adjust their execFile callback behavior or add exitCode === 0
assertions in tests that expect success.
🧹 Nitpick comments (4)
patches/sirupsen/logrus/terminal_check_appengine.go (1)

10-12: Consider returning false instead of true.

Always returning true means WASI outputs will include terminal escape codes even when piped/redirected. Returning false would be safer - no terminal formatting when terminal state is unknown.

♻️ Proposed fix
 func checkIfTerminal(w io.Writer) bool {
-	return true
+	return false
 }
build-wasi.sh (1)

1-6: Harden script safety checks.
Consider set -euo pipefail and a quick vendor dir check for clearer failures.

♻️ Suggested change
-#!/bin/bash
-set -e
+#!/bin/bash
+set -euo pipefail
+
+if [[ ! -d vendor ]]; then
+  echo "vendor/ not found; run 'go mod vendor' first."
+  exit 1
+fi
patches/chzyer/readline/term_wasip1.go (1)

31-37: Consider signaling “no terminal” instead of a fixed size.
Returning (80,24,nil) may trigger TTY paths. If terminal I/O is unsupported in WASI, returning an error (or 0,0) is safer.

♻️ Suggested change
-	return 80, 24, nil // Returning a standard terminal size of 80x24
+	return 0, 0, errors.New("unsupported on WASI")
test/test.js (1)

29-33: Use process.execPath for CLI runs.
Guarantees same Node version across environments; avoids PATH surprises.

♻️ Suggested change
-    execFile("node", [cliPath, ...args], { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
+    execFile(process.execPath, [cliPath, ...args], { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
       resolve({ error, stdout, stderr, exitCode: error?.code || 0 });
     });

Comment on lines +13 to +18
const wasi = new WASI({
version: "preview1",
args: ["regula", ...args],
env: process.env,
preopens: { "/": "/" },
returnOnExit: true,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

WASI preopens security best practices restrict filesystem access

💡 Result:

WASI preopens: security best practices to restrict filesystem access

  • Default-deny: don’t preopen anything unless you must. WASI modules have no filesystem access unless the host explicitly supplies preopened directory handles. (docs.rs)
  • Follow least privilege on scope (which directories). Preopen the smallest directory subtree the module needs (avoid broad roots like /, your home dir, or project root) because the module can access the whole contents of each preopened directory. (docs.wasmtime.dev)
  • Follow least privilege on rights (read vs write). When you preopen, set directory and file permissions to read-only unless writes are required; WASI hosts (e.g., Wasmtime) expose separate dir/file permission masks for this. (docs.wasmtime.dev)
  • Rely on the capability boundary—don’t “escape-proof” with path string checks alone. WASI’s model is: libc resolves absolute paths by translating them into a (preopened dir fd + relative path) and then using openat-style operations; access fails if no matching preopen capability exists. (sources.debian.org)
  • Assume .. traversal is blocked, but still keep preopens tight. Wasmtime notes that WASI prevents traversing outside a preopened directory (e.g., using ..). This is a safety net, not a reason to preopen large trees. (docs.rs)
  • Use dedicated “scratch” directories for writes. If a module needs temporary output, preopen a newly created empty directory (or per-tenant/per-request directory) rather than granting write access to existing host paths. (This follows directly from preopens granting access to the directory contents.) (docs.wasmtime.dev)
  • Minimize extra WASI surface area that comes with filesystem access. Security advisories show that some issues are only reachable when the guest can open additional file descriptors—commonly possible when a filesystem directory is preopened—so avoid preopens for untrusted modules unless required, and keep runtimes updated. (advisories.gitlab.com)

If you tell me your runtime/host (Wasmtime, Wasmer, Node, browsers, etc.) and whether you’re using WASI Preview1 or WASI 0.2/components, I can translate this into concrete configuration examples (CLI flags / embedding code) for your environment.

Citations:


🏁 Script executed:

fd cli.js

Repository: nonfx/regula

Length of output: 62


🏁 Script executed:

cat -n cli.js

Repository: nonfx/regula

Length of output: 1081


Restrict WASI preopens to limit filesystem access.

Line 17 maps / to /, exposing the entire host filesystem. WASI best practices recommend preopening only the smallest directory subtree needed. For a CLI tool, process.cwd() is appropriate—it limits access to where the user runs the command.

Suggested change
-const wasi = new WASI({
+const preopenDir = process.cwd();
+const wasi = new WASI({
   version: "preview1",
   args: ["regula", ...args],
   env: process.env,
-  preopens: { "/": "/" },
+  preopens: { "/": preopenDir },
   returnOnExit: true,
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const wasi = new WASI({
version: "preview1",
args: ["regula", ...args],
env: process.env,
preopens: { "/": "/" },
returnOnExit: true,
const preopenDir = process.cwd();
const wasi = new WASI({
version: "preview1",
args: ["regula", ...args],
env: process.env,
preopens: { "/": preopenDir },
returnOnExit: true,
});
🤖 Prompt for AI Agents
In `@cli.js` around lines 13 - 18, The WASI instance currently preopens the entire
host filesystem via preopens: { "/": "/" } in the new WASI(...) call; change
that to only expose the current working directory by replacing the preopens
mapping with a single subtree like preopens: { ".": process.cwd() } (i.e.,
update the new WASI(...) creation in cli.js so the WASI constructor maps only
process.cwd() instead of "/").

Comment on lines +12 to +26
* @param {string} options.inputType - Input type: auto, tf, tf-plan, cfn, k8s, arm
* @param {string} options.format - Output format: json, text, table, sarif, junit, tap
* @param {string[]} options.include - Additional rego files to include
* @param {string[]} options.only - Only run specific rules
* @param {string[]} options.exclude - Exclude specific rules
* @returns {Promise<Object>} - Regula output
*/
export async function runRegula(paths, options = {}) {
const pathArray = Array.isArray(paths) ? paths : [paths];

const args = ["run", "--format", "json"];

if (options.inputType) {
args.push("--input-type", options.inputType);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

options.format is documented but ignored.

options.format is advertised yet args always uses JSON, so callers can’t request other formats. Either honor the option or drop it from the public docs.

✅ Minimal fix
-  const args = ["run", "--format", "json"];
+  const args = ["run", "--format", options.format ?? "json"];
🤖 Prompt for AI Agents
In `@index.js` around lines 12 - 26, The function runRegula documents
options.format but always forces JSON; update argument construction in runRegula
so args uses options.format when provided (fall back to "json" if not), i.e.,
replace the hardcoded ["run","--format","json"] with logic that sets "--format"
to options.format or "json" before pushing it into args; reference runRegula,
the args array, and options.format to locate and change the code.

Comment on lines +72 to +82
// Enabled checks whether the local systemd journal is available for logging.
func Enabled() bool {
onceConn.Do(initConn)

if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil {
return false
}

if _, err := net.Dial("unixgram", journalSocket); err != nil {
return false
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "journal.go" patches/

Repository: nonfx/regula

Length of output: 100


🏁 Script executed:

cat -n "patches/coreos/go-systemd/journal/journal.go" | sed -n '65,90p'

Repository: nonfx/regula

Length of output: 908


🏁 Script executed:

cat -n "patches/coreos/go-systemd/journal/journal.go" | head -100

Repository: nonfx/regula

Length of output: 3532


🏁 Script executed:

cat -n "patches/coreos/go-systemd/journal/journal.go" | sed -n '100,200p'

Repository: nonfx/regula

Length of output: 3585


🏁 Script executed:

rg -n "func initConn" "patches/coreos/go-systemd/journal/journal.go" -A 20

Repository: nonfx/regula

Length of output: 353


Enabled() leaks a file descriptor on every check.

Line 80 dials unixgram for probe but never closes—repeated Enabled() calls exhaust FDs. Capture and close before returning.

Suggested fix
 if _, err := net.Dial("unixgram", journalSocket); err != nil {
 	return false
 }

+conn, err := net.Dial("unixgram", journalSocket)
+if err != nil {
+	return false
+}
+_ = conn.Close()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Enabled checks whether the local systemd journal is available for logging.
func Enabled() bool {
onceConn.Do(initConn)
if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil {
return false
}
if _, err := net.Dial("unixgram", journalSocket); err != nil {
return false
}
// Enabled checks whether the local systemd journal is available for logging.
func Enabled() bool {
onceConn.Do(initConn)
if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil {
return false
}
conn, err := net.Dial("unixgram", journalSocket)
if err != nil {
return false
}
_ = conn.Close()
🤖 Prompt for AI Agents
In `@patches/coreos/go-systemd/journal/journal.go` around lines 72 - 82, Enabled()
currently calls net.Dial("unixgram", journalSocket) but discards the resulting
connection, leaking a file descriptor on each probe; update Enabled() to capture
the returned connection (e.g., conn, err := net.Dial(...)), check err as now,
and ensure the connection is closed (conn.Close()) before returning (use a defer
immediately after a successful dial or an explicit Close prior to return) so
repeated calls don't exhaust FDs; keep the existing checks involving
onceConn.Do(initConn) and unixConnPtr unchanged.

Comment on lines +120 to +137
// Large log entry, send it via tempfile and ancillary-fd.
file, err := tempFd()
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, data)
if err != nil {
return err
}
// @nonfx - Disabled for wasip1 compilation
// rights := syscall.UnixRights(int(file.Fd()))
// _, _, err = conn.WriteMsgUnix([]byte{}, rights, socketAddr)
// if err != nil {
// return err
// }

return nil
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -path "*coreos/go-systemd/journal/journal.go" -type f

Repository: nonfx/regula

Length of output: 102


🏁 Script executed:

cat -n ./patches/coreos/go-systemd/journal/journal.go | sed -n '100,150p'

Repository: nonfx/regula

Length of output: 1719


🏁 Script executed:

# Check the function signature and broader context
cat -n ./patches/coreos/go-systemd/journal/journal.go | sed -n '75,110p'

Repository: nonfx/regula

Length of output: 1450


Large journal entries are silently dropped.

When WriteMsgUnix hits EMSGSIZE/ENOBUFS, you create a temp file but the ancillary-FD send is commented out, then return nil. That's silent data loss. Use build-tagged helpers: real send on non-WASI, explicit error on WASI.

✅ Directional fix (helper + build tags)
-	// `@nonfx` - Disabled for wasip1 compilation
-	// rights := syscall.UnixRights(int(file.Fd()))
-	// _, _, err = conn.WriteMsgUnix([]byte{}, rights, socketAddr)
-	// if err != nil {
-	// 	return err
-	// }
+	if err := sendWithFD(conn, socketAddr, file); err != nil {
+		return err
+	}

Then add:

  • sendWithFD_unix.go (//go:build !wasip1): uses syscall.UnixRights + WriteMsgUnix
  • sendWithFD_wasip1.go (//go:build wasip1): returns a clear "unsupported" error
🤖 Prompt for AI Agents
In `@patches/coreos/go-systemd/journal/journal.go` around lines 120 - 137, The
temp file fallback currently drops large journal entries because the
ancillary-FD send using conn.WriteMsgUnix is commented out; replace that silent
noop with a build-tagged helper (e.g., sendWithFD or sendFD) and call it where
the commented code is: create sendWithFD_unix.go (//go:build !wasip1) that
constructs syscall.UnixRights(int(file.Fd())) and calls conn.WriteMsgUnix to
send the FD and returns any error, and create sendWithFD_wasip1.go (//go:build
wasip1) that returns a clear "unsupported on wasip1" error; update the code
around tempFd/conn.WriteMsgUnix to call this helper and propagate its error
instead of returning nil so large entries are either sent or fail explicitly.

Comment on lines +11 to +17
func checkIfTerminal(w io.Writer) bool {
switch v := w.(type) {
case *os.File:
return isTerminal(int(v.Fd()))
default:
return false
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for isTerminal function definition
rg -n 'func isTerminal' patches/

Repository: nonfx/regula

Length of output: 38


🏁 Script executed:

# Check the file structure in logrus patches
ls -la patches/sirupsen/logrus/ | head -20

Repository: nonfx/regula

Length of output: 319


🏁 Script executed:

# Get full content of the file in question
cat -n patches/sirupsen/logrus/terminal_check_notappengine.go

Repository: nonfx/regula

Length of output: 492


🏁 Script executed:

# Search more broadly for isTerminal
rg -n 'isTerminal' patches/

Repository: nonfx/regula

Length of output: 146


🏁 Script executed:

# Check what's in the entire logrus directory including hidden files
find patches/sirupsen/logrus/ -type f -name "*.go"

Repository: nonfx/regula

Length of output: 162


🏁 Script executed:

# Look at the appengine version for comparison
cat -n patches/sirupsen/logrus/terminal_check_appengine.go

Repository: nonfx/regula

Length of output: 288


🏁 Script executed:

# Search the entire repo for isTerminal definition
rg -n 'func isTerminal' .

Repository: nonfx/regula

Length of output: 38


🏁 Script executed:

# Check if there's an upstream logrus directory (non-patched)
find . -type d -name logrus | head -10

Repository: nonfx/regula

Length of output: 81


🏁 Script executed:

# Look for any terminal_check files outside patches
find . -name "terminal_check*.go" -type f

Repository: nonfx/regula

Length of output: 166


🏁 Script executed:

# Check if there are any imports or dependencies that might provide isTerminal
rg -n 'import|isTerminal' patches/sirupsen/logrus/terminal_check_notappengine.go

Repository: nonfx/regula

Length of output: 102


🏁 Script executed:

# Search for terminal_check files in the whole repo to understand the patch structure
find . -path ./patches -prune -o -name "terminal_check*.go" -type f -print

Repository: nonfx/regula

Length of output: 38


🏁 Script executed:

# Look at git history or any comments that might explain the patch
head -50 patches/sirupsen/logrus/terminal_check_notappengine.go

Repository: nonfx/regula

Length of output: 366


isTerminal function is undefined – code won't compile.

This file calls isTerminal(int(v.Fd())) on line 14 but the function is not defined anywhere in the codebase. No imports bring it in either. The patch is incomplete – either the function definition is missing from the patches, or this needs additional files from upstream logrus to work.

🧰 Tools
🪛 GitHub Check: Test with Go 1.24.11

[failure] 14-14:
undefined: isTerminal

🤖 Prompt for AI Agents
In `@patches/sirupsen/logrus/terminal_check_notappengine.go` around lines 11 - 17,
The call to isTerminal(int(v.Fd())) in checkIfTerminal is undefined; add a
concrete implementation or delegate to the standard term package. Import
"golang.org/x/term" (or "golang.org/x/crypto/ssh/terminal" if you prefer) and
replace/implement isTerminal to call term.IsTerminal(fd) (or add a local
isTerminal wrapper that calls term.IsTerminal) so checkIfTerminal compiles;
ensure the import is added and referenced functions are checkIfTerminal and
isTerminal (and types io.Writer/os.File) so the linker can resolve the symbol.

Comment on lines +36 to +49
function runWasi(args) {
return new Promise((resolve, reject) => {
execFile("node", [cliPath, ...args], { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
resolve({ error, stdout, stderr, exitCode: error?.code || 0 });
});
});
}

function runBinary(args) {
return new Promise((resolve, reject) => {
execFile(binaryPath, args, { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
resolve({ error, stdout, stderr, exitCode: error?.code || 0 });
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's see the structure of the test file
wc -l test/test-wasi.js

Repository: nonfx/regula

Length of output: 77


🏁 Script executed:

# Read the full test file to understand the test structure and assertions
cat -n test/test-wasi.js

Repository: nonfx/regula

Length of output: 8429


🏁 Script executed:

# Let's also check if there are other test files that might provide context
git ls-files test/

Repository: nonfx/regula

Length of output: 135


🏁 Script executed:

# Check if exitCode is asserted anywhere in the test file
rg "exitCode" test/test-wasi.js -A 2 -B 2

Repository: nonfx/regula

Length of output: 510


🏁 Script executed:

# Double-check: are there any other error handling patterns or exitCode checks elsewhere?
rg "\.error|exitCode|assert" test/test-wasi.js | grep -E "(error|exitCode)"

Repository: nonfx/regula

Length of output: 239


Add exitCode assertions—tests can pass with both binaries failing identically.

runWasi and runBinary always resolve, never reject. Tests mostly compare stdout only, so missing/invalid binaries pass silently. Add exitCode === 0 checks in success tests, or reject on error in the helpers.

Test 10's comment claims "Exit codes match" but only checks JSON parse results, not actual exit codes.

Minimal fix (shared helper)
+function assertOk(result, label) {
+  assert.strictEqual(result.exitCode, 0, `${label} exit code should be 0`);
+  assert.ifError(result.error);
+}

Apply in success cases, e.g.:

-    const wasi = await runWasi(["version"]);
-    const binary = await runBinary(["version"]);
+    const wasi = await runWasi(["version"]);
+    const binary = await runBinary(["version"]);
+    assertOk(wasi, "wasi");
+    assertOk(binary, "binary");
🤖 Prompt for AI Agents
In `@test/test-wasi.js` around lines 36 - 49, runWasi and runBinary currently
always resolve even when the child process fails, so tests can pass by comparing
stdout alone; update the helpers or tests so failures are surfaced: modify
runWasi and runBinary to reject when the execFile callback reports an error (use
error and error.code) so callers receive a rejected promise on non-zero exit, or
alternatively keep the helpers but add explicit assertions in success tests
(e.g., Test 10) to assert result.exitCode === 0 before checking stdout/JSON;
locate the functions runWasi and runBinary and adjust their execFile callback
behavior or add exitCode === 0 assertions in tests that expect success.

Comment on lines +178 to +187
// Test 10: Exit codes match
await test("Exit codes match for failures", async () => {
const wasi = await runWasi(["run", "--format", "json", join(fixturesDir, "insecure.tf")]);
const binary = await runBinary(["run", "--format", "json", join(fixturesDir, "insecure.tf")]);

// Both should have non-zero exit for files with violations
const wasiHasFailures = JSON.parse(wasi.stdout).summary.rule_results.FAIL > 0;
const binaryHasFailures = JSON.parse(binary.stdout).summary.rule_results.FAIL > 0;

assert.strictEqual(wasiHasFailures, binaryHasFailures, "Both should detect failures");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n test/test-wasi.js | sed -n '170,190p'

Repository: nonfx/regula

Length of output: 1207


🏁 Script executed:

# Find the runWasi and runBinary function implementations
rg -A 10 "function runWasi|const runWasi" test/test-wasi.js

Repository: nonfx/regula

Length of output: 478


🏁 Script executed:

# Also check for runBinary
rg -A 10 "function runBinary|const runBinary" test/test-wasi.js

Repository: nonfx/regula

Length of output: 377


Test doesn't actually compare exit codes despite its name.

The test is called "Exit codes match for failures" but only checks if both detect failures. Add the exit code assertion.

    assert.strictEqual(wasiHasFailures, binaryHasFailures, "Both should detect failures");
+   assert.strictEqual(wasi.exitCode, binary.exitCode, "Exit codes should match");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Test 10: Exit codes match
await test("Exit codes match for failures", async () => {
const wasi = await runWasi(["run", "--format", "json", join(fixturesDir, "insecure.tf")]);
const binary = await runBinary(["run", "--format", "json", join(fixturesDir, "insecure.tf")]);
// Both should have non-zero exit for files with violations
const wasiHasFailures = JSON.parse(wasi.stdout).summary.rule_results.FAIL > 0;
const binaryHasFailures = JSON.parse(binary.stdout).summary.rule_results.FAIL > 0;
assert.strictEqual(wasiHasFailures, binaryHasFailures, "Both should detect failures");
// Test 10: Exit codes match
await test("Exit codes match for failures", async () => {
const wasi = await runWasi(["run", "--format", "json", join(fixturesDir, "insecure.tf")]);
const binary = await runBinary(["run", "--format", "json", join(fixturesDir, "insecure.tf")]);
// Both should have non-zero exit for files with violations
const wasiHasFailures = JSON.parse(wasi.stdout).summary.rule_results.FAIL > 0;
const binaryHasFailures = JSON.parse(binary.stdout).summary.rule_results.FAIL > 0;
assert.strictEqual(wasiHasFailures, binaryHasFailures, "Both should detect failures");
assert.strictEqual(wasi.exitCode, binary.exitCode, "Exit codes should match");
🤖 Prompt for AI Agents
In `@test/test-wasi.js` around lines 178 - 187, The test named "Exit codes match
for failures" currently only compares failure detection; update it to also
assert the exit codes from runWasi and runBinary are equal and non-zero when
failures are present: after computing wasiHasFailures and binaryHasFailures, add
assertions comparing wasi.exitCode and binary.exitCode (e.g.,
assert.strictEqual(wasi.exitCode, binary.exitCode)) and assert that when
wasiHasFailures is true the exit code is !== 0 (and similarly for binary) so
both the equality and non-zero behavior are checked for the wasi and binary
results returned by runWasi and runBinary.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@README.md`:
- Around line 155-170: The fenced code block under the "Available Commands"
section (the block starting with "regula [command]") is missing a language tag
which markdownlint flags; update the opening triple-backticks to include a
language (e.g., change ``` to ```text) so the block becomes ```text followed by
the existing "regula [command]" content and the closing ```; no other content
change is needed.

Comment on lines +155 to 170
```
regula [command]

Once you've built the binary, execute the following to run tests:
Commands:
run Evaluate rules against infrastructure as code
test Run OPA test with Regula
repl Start an interactive session for testing rules
init Create a new Regula configuration file
show Show debug information
version Print version information
completion Generate shell autocompletion script

Flags:
-h, --help Help for regula
-v, --verbose Verbose output
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language to the fenced code block.
The command block under “Available Commands” is missing a language tag; markdownlint will flag it.

Suggested fix
-```
+```text
 regula [command]

 Commands:
   run               Evaluate rules against infrastructure as code
   test              Run OPA test with Regula
   repl              Start an interactive session for testing rules
   init              Create a new Regula configuration file
   show              Show debug information
   version           Print version information
   completion        Generate shell autocompletion script

 Flags:
   -h, --help        Help for regula
   -v, --verbose     Verbose output
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
regula [command]
Once you've built the binary, execute the following to run tests:
Commands:
run Evaluate rules against infrastructure as code
test Run OPA test with Regula
repl Start an interactive session for testing rules
init Create a new Regula configuration file
show Show debug information
version Print version information
completion Generate shell autocompletion script
Flags:
-h, --help Help for regula
-v, --verbose Verbose output
```
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 155-155: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@README.md` around lines 155 - 170, The fenced code block under the "Available
Commands" section (the block starting with "regula [command]") is missing a
language tag which markdownlint flags; update the opening triple-backticks to
include a language (e.g., change ``` to ```text) so the block becomes ```text
followed by the existing "regula [command]" content and the closing ```; no
other content change is needed.

@NikhilVerma
Copy link
Author

@codrabbitai review

NikhilVerma and others added 3 commits February 4, 2026 18:51
- fsnotify: Return ErrNotSupported instead of nil for unsupported methods
- cli.js: Add comment explaining why preopens '/' is necessary
- index.js: Remove misleading format option from docs, add noBuiltIns/noIgnore/varFiles options
- Remove unused patches (go-systemd, terraform.patch)
- Update build script to remove go-systemd reference

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@README.md`:
- Around line 1-27: Update the README's stated versions: change the OPA line
that currently reads "OPA upgraded from v0.45.1 to v1.12.2 (latest)" to "OPA
upgraded from v0.45.1 to v1.11.0" (remove the "(latest)" tag), and update the Go
line from "Go upgraded to 1.24.11 with stdlib CVE fixes" to "Go upgraded to
1.25.6 with stdlib CVE fixes" so the documentation matches the correct target
versions.

@NikhilVerma NikhilVerma merged commit e32a6d0 into master Feb 4, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants